package com.flute.haflute.agent.manage;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.flute.haflute.common.ClusterConstants.COMMAND;
import com.flute.haflute.slave.Child;

public class JvmManager {
	private static Logger logger = LoggerFactory.getLogger(JvmManager.class);

	private static Map<String, JvmManager> jvmManagers = new HashMap<String, JvmManager>();

	private Map<JvmId, Process> jvms = new HashMap<JvmId, Process>();

	private Process process = null; // sub process used to execute the command

	private String osName = System.getProperty("os.name");

	IjvmScript script = null;

	public static void closeAll() {
		Collection<JvmManager> allJvmManager = jvmManagers.values();
		for (JvmManager manager : allJvmManager) {
			Collection<Process> processes = manager.getAllProcess();
			for (Process process : processes) {
				sendCommand2OneJvm(process, COMMAND.SHUTDOWN);
//				process.destroy();
			}
			manager.clearProcessCache();
		}
	}
	public static Process findProcessFromJvmManagerSet(JvmId jvmId) {
		Collection<JvmManager> allJvmManager = jvmManagers.values();
		for (JvmManager manager : allJvmManager) {
			Process process = manager.getProcess(jvmId);
			if (process != null) {
				return process;
			}
		}
		return null;
	}
	public static synchronized JvmManager getInstance(String mainClassName, File libPath) {
		JvmManager jvmManager = jvmManagers.get(mainClassName);
		if (jvmManager == null) {
			jvmManager = new JvmManager(mainClassName, libPath);
			jvmManagers.put(mainClassName, jvmManager);
			logger.debug("new jvmManager spawned for: {}", mainClassName);
		}
		return jvmManager;
	}

	public static void main(String args[]) throws Exception {
//		JvmManager run = JvmManager.getInstance("com.cpkf.yyjd.jvm.test", new File("./lib"));
//		JvmId jvmid = run.spawnChildJvm(null,new String[0], new String[0]);
//		// JvmId jvmid2 = run.spawnChildJvm(new
//		// File("./lib"),"com.cpkf.yyjd.jvm.test");
//		if (jvmid != null) {
//			// System.out.println("1111111111111 =" + run.getProcess(jvmid));
//			// System.out.println("2222222222222 =" + run.getProcess(jvmid2));
//		}
	}

	public static void sendCommand2AllChilds(String cmd) {
		JvmManager manager = getInstance(Child.class.getName(), new File(""));
		for (Process process : manager.getAllProcess()) {
			BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
			try {
				bufferedWriter.newLine();
				bufferedWriter.write(cmd);
				bufferedWriter.newLine();
				bufferedWriter.flush();
//				bufferedWriter.close();
			} catch (IOException e) {
				logger.info("send Command to child error, jvm may already be down", e);
			}
		}
	}

	public static void sendCommand2OneJvm(Process process, String cmd) {
		BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
		try {
			bufferedWriter.newLine();
			bufferedWriter.write(cmd);
			bufferedWriter.newLine();
			bufferedWriter.flush();
		} catch (IOException e) {
			logger.info("send Command to child error, jvm may already be down", e);
		}
	}
	private JvmManager(String mainClassName, File classpath) {
		if (osName.startsWith("Windows")) {
			script = new WindowsJvmScript(mainClassName, classpath);
		} else {
			script = new UnixJvmScript(mainClassName, classpath);
		}
	}

	public synchronized Collection<Process> getAllProcess() {
		return jvms.values();
	}
	
	public synchronized Map<JvmId, Process> processMap() {
		return jvms;
	}
	
	public synchronized void clearProcessCache() {
		jvms.clear();
	}

	/**
	 * 获取命令集
	 */
	private String[] getExecString(IjvmScript script, String[] jvmParams, String[] args) {
		List<String> commandList = script.getCommandList(jvmParams);
		for (String arg : args) {
			commandList.add(arg);
		}
		printFinalCommand2OS(commandList);
		String[] commands = commandList.toArray(new String[commandList.size()]);
		return commands;
	}

	private void printFinalCommand2OS(List<String> commandList) {
		StringBuilder strb = new StringBuilder();
		for(String command : commandList) {
			strb.append(command).append(" ");
		}
		logger.info("one JVM with command\n{}", strb);
	}
	public Process getProcess(JvmId jvmId) {
		return jvms.get(jvmId);
	}

	private void setProcess(JvmId key, Process process) {
		this.process = process;
		jvms.put(key, process);
	}

	/**
	 * 启动JVM
	 * 
	 * @param scripts
	 * @param workdir
	 * @return
	 * @throws Exception
	 */
	public JvmId spawnChildJvm(File workdir, String[] jvmParams, String[] args) throws Exception {
		if (workdir != null) {
			script.setWorkpath(workdir);
		}
		ProcessBuilder builder = new ProcessBuilder(getExecString(script,jvmParams, args));
		builder.directory(script.getWorkpath());
		builder.redirectErrorStream(true);
		process = builder.start();
		JvmId jvmid = new JvmId();
		setProcess(jvmid, process);

		return jvmid;
	}
}
